package com.kylin.common.core.exception;

import com.kylin.common.core.beans.GlobalResultCode;
import com.kylin.common.core.beans.ResultBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ValidationException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 异常处理器
 *
 * @author Mark sunlightcs@gmail.com
 */
@Slf4j
@RestControllerAdvice
@Order(0)
public class ExceptionHandlerAdvice {

	@ExceptionHandler(DuplicateKeyException.class)
	public ResultBean<Serializable> handleDuplicateKeyException(DuplicateKeyException e){
		log.error(e.getMessage(), e);
		return new ResultBean<>(GlobalResultCode.BAD_REQUEST.getCode(),"数据库中已存在该记录");
	}

	/**
	 * BeanValidate抛出异常
	 * Validator验证框架抛出的业务逻辑异常
	 */
	@ExceptionHandler(ValidationException.class)
	public ResultBean<Serializable> handleViolationException(ValidationException e){
		log.error(e.getMessage());
		/**String message = ((ConstraintViolationException) e).getConstraintViolations().iterator().next().getMessage();*/
		String message = e.getLocalizedMessage();
		return new ResultBean<>(GlobalResultCode.BAD_REQUEST.getCode(), message);
	}

	/**
	 * 处理请求参数格式错误 @RequestBody上使用@Valid 实体上使用@NotNull等，验证失败后抛出的异常是MethodArgumentNotValidException异常
	 * Validator验证框架抛出的业务逻辑异常
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResultBean<Serializable> handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
		log.error(e.getMessage());
		List<FieldError> list = e.getBindingResult().getFieldErrors();
		List<String> message = new ArrayList<>();
		for(FieldError error: list) {
			message.add(error.getDefaultMessage());
		}
		return new ResultBean<>(GlobalResultCode.BAD_REQUEST.getCode(),
				String.format("%s:[%s]", CheckException.CHECK_INCORRECT_FORMAT, String.join(",", message)));
	}
	
	/**
	 * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
	 */
	@ExceptionHandler(BindException.class)
	public ResultBean<Serializable> handleBindException(BindException e) {
		log.error(e.getMessage());
		List<FieldError> list = e.getBindingResult().getFieldErrors();
		List<String> message = new ArrayList<>();
		for(FieldError error: list) {
			message.add(error.getDefaultMessage());
		}
		return new ResultBean<>(GlobalResultCode.BAD_REQUEST.getCode(),
				String.format("%s:[%s]", CheckException.CHECK_INCORRECT_FORMAT, String.join(",", message)));
	}

	/**
	 * 校验异常处理
	 */
	@ExceptionHandler(CheckException.class)
	public ResultBean<Serializable> handleCheckException(CheckException e){
		log.error(e.getMessage(), e);
		return new ResultBean<>(e.getCode(),e.getMessage());
	}

	/**
	 * 参数转换异常
	 * 参数中传入的String类型的字段 在接收时 该映射字段时Integer类型
	 * 当 Controller 曾 @RequestBody 接受参数时 调用接口时没有传参数时也会抛出此异常
	 */
	@ExceptionHandler({HttpMessageNotReadableException.class})
	public ResultBean<Serializable> messageExceptionHandler(HttpMessageNotReadableException e) {
		return new ResultBean<>(GlobalResultCode.BAD_REQUEST.getCode(),"参数转换异常，请检查参数！");
	}

	/**
	 * 系统业务异常处理（防止拦截器未拦击到的错误）
	 */
	@ExceptionHandler(ServiceException.class)
	public ResultBean<Serializable> handleServiceException(ServiceException e){
		log.error(e.getMessage(), e);
		return new ResultBean<>(e.getCode(),e.getMessage());
	}


	/**
	 * 基于baseException捕获
	 */
	@ExceptionHandler(BaseException.class)
	public ResultBean<Serializable> handleServiceException(BaseException e){
		log.error(e.getMessage(), e);
		return new ResultBean<>(e.getCode(),e.getMessage());
	}

	/**
	 * 其他系统级异常
	 */
	@ExceptionHandler(Exception.class)
	@ResponseBody
	public ResultBean<Serializable> handleException(HttpServletRequest request,Exception e){
		log.error("{}\n,{}", request.getRequestURI(),request.getParameterMap());
		log.error(e.getMessage(), e);
		return new ResultBean<>(GlobalResultCode.UNKNOWN_EXCEPTION);
	}

}
